package com.itheima.dbsharding.month.tool;

import com.google.common.base.Preconditions;

import java.util.Calendar;

/**
 * @author jiangta
 * @version 1.0
 * @date 2020/5/8 1:24 下午
 */
public class KeyGeneratorTools {

    private static final long EPOCH ;
    //服务器好所占位数 2^10
    private static final long WORKER_ID_BITS = 10L;
    //序列号所占位数
    private static final long SEQUENCE_BITS = 12L;
    //生成序列的掩码
    private static final long SEQUENCE_MASK = 4095L;
    //机器id 的偏移位数
    private  static final long WORKER_ID_LEFT_SHIFT_BITS = 12L;
    //时间戳左移位数
    private static final long TIMESTAMP_LEFT_SHIFT_BITS = 22L;
    //work id 的最大值
    private static final long WORKER_ID_MAX_VALUE = 1024L;
    //设置的机器id
    private static long workerId;
    //并发情况下 生成的时间序列
    private long sequence;
    //最新时间
    private long lastTime;
    public static void setWorkerId(long workerId) {
        if (workerId > WORKER_ID_MAX_VALUE || workerId < 0) {
            throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", workerId));
        }
        workerId = workerId;
    }
    public KeyGeneratorTools(){

    }
    public synchronized long getGeneratorKey(){
        //获取系统当前时间戳
        long currentMillis = System.currentTimeMillis();
        //检查时间 如果时间小于 最新时间则抛出传参异常
        Preconditions.checkState(this.lastTime <= currentMillis, "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", new Object[]{this.lastTime, currentMillis});
        if (0L == (this.sequence = this.sequence + 1L & SEQUENCE_MASK)) {
            currentMillis = this.waitUntilNextTime(currentMillis);
        }
        this.lastTime = currentMillis;
        //当前时间-起始时间 左移22位 wokerId 左移10位 后 三者做或运算 00为0 01为1 11位1 10未1
        return currentMillis - EPOCH << TIMESTAMP_LEFT_SHIFT_BITS | workerId << WORKER_ID_LEFT_SHIFT_BITS | this.sequence;
    }
    private long waitUntilNextTime(long lastTime) {//如果时间相同者进行等待
        long time;
        for(time = System.currentTimeMillis(); time <= lastTime; time = System.currentTimeMillis()) {
        }

        return time;
    }
    static {
        Calendar calendar = Calendar.getInstance();
        calendar.set(2016, 10, 1);
        calendar.set(11, 0);
        calendar.set(12, 0);
        calendar.set(13, 0);
        calendar.set(14, 0);
        EPOCH = calendar.getTimeInMillis();
    }
}
